using System;
using GameFrameworkLibraly.BaseObjects;
using GameFrameworkLibraly.Controls.Interfaces;
using GameFrameworkLibraly.Helpers.GeometryHelpers;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input.Touch;

namespace GameFrameworkLibraly.Controls
{
    public abstract class Control:IDrawable,IUpdateable,IProcessTouch,IProcessGesture
    {
        protected MyGame Game;

        protected Control(MyGame game)
        {
            Game = game;
            _visible = true;
            _enabled = true;
        }

        private bool _visible;
        private bool _enabled;

        /// <summary>
        /// Indicates if user is holding his finger on this control
        /// </summary>
        public bool IsPressed { get; protected set; }

        private Color _backColorFilter = Color.White;
        private Color _foreColor = Color.White;

        /// <summary>
        /// Background texture
        /// </summary>
        public Texture2D BackgroundTexture { get; protected set; }

        /// <summary>
        /// Parent Control for this control
        /// </summary>
        public Control ParentControl { get; set; }

        /// <summary>
        /// Draws the IDrawable. Reference page contains links to related conceptual articles.
        /// </summary>
        /// <param name="gameTime">Snapshot of the game's timing state.</param>
        public virtual void Draw(GameTime gameTime)
        {
        }

        /// <summary>
        /// The order in which to draw this object relative to other objects. Objects with a lower value are drawn first.
        /// </summary>
        public virtual int DrawOrder
        {
            get { return 0; }
        }

        /// <summary>
        /// Defines if control must be drawn
        /// </summary>
        public bool Visible
        {
            get { return _visible; }
            set
            {
                bool visibleChanged = value != _visible;
                _visible = value;
                if (visibleChanged)
                    InvokeVisibleChanged(EventArgs.Empty);
            }
        }

        
        public event EventHandler<EventArgs> DrawOrderChanged;

        /// <summary>
        /// Occures after the visibility of the control was changed
        /// </summary>
        public event EventHandler<EventArgs> VisibleChanged;

        protected void InvokeVisibleChanged(EventArgs e)
        {
            EventHandler<EventArgs> handler = VisibleChanged;
            if (handler != null) handler(this, e);
        }

        /// <summary>
        /// Color filter for back texture
        /// </summary>
        public Color BackColorFilter
        {
            get { return _backColorFilter; }
            protected set { _backColorFilter = value; }
        }

        /// <summary>
        /// Color filter for fore texture
        /// </summary>
        public Color ForeColor
        {
            get { return _foreColor; }
            set { _foreColor = value; }
        }

        /// <summary>
        /// Draws the control
        /// </summary>
        /// <param name="spriteBatch"></param>
        public abstract void Draw(SpriteBatch spriteBatch);

        /// <summary>
        /// Updates state of the control
        /// </summary>
        /// <param name="gameTime"></param>
        public abstract void Update(GameTime gameTime);


        protected int? TouchID;

        /// <summary>
        /// Handles a touch and changes the state of this component if needed
        /// </summary>
        /// <param name="touchLocation">Touch Location</param>
        /// <returns>True, if touch was handled</returns>
        public virtual bool ProcessTouch(TouchLocation touchLocation)
        {
            if(!Enabled)
                return false;
            bool touchHandled = false;
            bool isInside = DestinationRectangle.Contains((int)touchLocation.Position.X, (int)touchLocation.Position.Y);
            switch (touchLocation.State)
            {

                case TouchLocationState.Pressed:
                    if (isInside)
                    {
                        InvokeClick(EventArgs.Empty);
                        IsPressed = true;
                        TouchID = touchLocation.Id;
                        touchHandled = true;
                    }
                    break;
                case TouchLocationState.Moved:
                    if (TouchID.HasValue && TouchID.Value == touchLocation.Id)
                    {
                        IsPressed = isInside;
                        touchHandled = true;
                    }
                    break;
                case TouchLocationState.Released:
                    if (TouchID.HasValue && TouchID.Value == touchLocation.Id)
                    {
                        TouchID = null;
                        IsPressed = false;
                        touchHandled = true;
                        if(isInside)
                            InvokeReleased(EventArgs.Empty);
                    }
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }


            return touchHandled;
        }

        /// <summary>
        /// Defines if control is enabled
        /// </summary>
        public bool Enabled
        {
            get { return _enabled; }
            set
            {
                bool enabledChanged = value != _enabled;
                _enabled = value;
                if (enabledChanged)
                    InvokeEnabledChanged(EventArgs.Empty);
            }
        }

        /// <summary>
        /// Indicates when the game component should be updated relative to other game components. Lower values are updated first.
        /// </summary>
        public virtual int UpdateOrder
        {
            get { return 0; }
        }

        public event EventHandler<EventArgs> EnabledChanged;
        

        public event EventHandler<EventArgs> UpdateOrderChanged;

        protected void InvokeEnabledChanged(EventArgs e)
        {
            EventHandler<EventArgs> handler = EnabledChanged;
            if (handler != null) handler(this, e);
        }

        private Rectangle _destinationRectangle;

        /// <summary>
        /// Defines rectangle where the control must be drawn
        /// </summary>
        public Rectangle DestinationRectangle
        {
            get { return _destinationRectangle; }
            set
            {
                _destinationRectangle = value;
                InvokeDestinationRectangleChanged(EventArgs.Empty);
            }
        }

        /// <summary>
        /// Occures when DestinationRectangle has been changed
        /// </summary>
        public event EventHandler DestinationRectangleChanged;

        protected void InvokeDestinationRectangleChanged(EventArgs e)
        {
            EventHandler handler = DestinationRectangleChanged;
            if (handler != null) handler(this, e);
        }

        /// <summary>
        /// Occures when user touches the control
        /// </summary>
        public event EventHandler<EventArgs> Click;

        protected void InvokeClick(EventArgs e)
        {
            EventHandler<EventArgs> handler = Click;
            if (handler != null) handler(this, e);
        }

        /// <summary>
        /// Occures when user releases the control
        /// </summary>
        public event EventHandler<EventArgs> Released;

        protected void InvokeReleased(EventArgs e)
        {
            EventHandler<EventArgs> handler = Released;
            if (handler != null) handler(this, e);
        }

        public event EventHandler Tapped;

        protected virtual void InvokeTapped(EventArgs e)
        {
            EventHandler handler = Tapped;
            if (handler != null) handler(this, e);
        }

        #region IProcessGesture

        public virtual bool ProcessGesture(GestureSample gestureSample)
        {
            if (!Enabled)
                return false;
            if(gestureSample.GestureType==GestureType.Tap)
            {
                if(DestinationRectangle.Contains(gestureSample.Position.ToPoint()))
                {
                    InvokeTapped(EventArgs.Empty);
                    return true;
                }
            }
            return false;
        }

        #endregion
    }
}
